﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Objects.DataClasses;
using System.Data.Objects;
using System.ServiceModel.DomainServices.EntityFramework;

namespace Agility.Web.Repositories.EntityFramework
{
    /// <summary>
    /// Provides EF specific implementation of the Repository pattern
    /// </summary>
    /// <typeparam name="T">The entity stored in this repository</typeparam>
    public class EntityFrameworkRepositoryBase<T> : IRepository<T> where T : EntityObject
    {
        private readonly ObjectContext objectContext;
        private ObjectSet<T> objectSet;

        public EntityFrameworkRepositoryBase(ObjectContext objectContext, ObjectSet<T> objectSet)
        {
            if (objectContext == null)
                throw new ArgumentNullException("objectContext");
            this.objectContext = objectContext;
            this.objectSet = objectSet;
        }

        protected ObjectContext ObjectContext { get { return this.objectContext; } }

        protected ObjectSet<T> ObjectSet
        {
            get
            {
                //if (this.objectSet == null)
                //    this.objectSet = this.objectContext.CreateObjectSet<T>();
                return this.objectSet;
            }
        }
        
        public virtual IQueryable<T> GetEntities()
        {
            return this.ObjectSet;
        }

        public virtual void Insert(T entity)
        {
            if (entity.EntityState == System.Data.EntityState.Detached)
            {
                try
                {
                    this.objectSet.AddObject(entity);
                }
                catch (Exception)
                {
                    
                    throw;
                }
            }
            else
                this.objectContext.ObjectStateManager.ChangeObjectState(entity, System.Data.EntityState.Added);
        }

        public virtual void Update(T entity)
        {
            object[] timestamp = null;
            foreach (var propertyInfo in typeof(T).GetProperties())
	        {
		        timestamp = propertyInfo.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.TimestampAttribute), false);
	        }
            if (timestamp.Length == 0)
                throw new InvalidOperationException("Entity has no property marked with the TimeStampAttribute");
            this.ObjectSet.AttachAsModified(entity);
        }

        public virtual void Update(T entity, T original)
        {
            if (original == null)
                throw new ArgumentNullException("original");
            this.ObjectSet.AttachAsModified(entity, original);
        }

        public virtual void Delete(T entity)
        {
            if (entity.EntityState != System.Data.EntityState.Detached)
                this.ObjectContext.ObjectStateManager.ChangeObjectState(entity, System.Data.EntityState.Deleted);
            else
            {
                this.ObjectSet.Attach(entity);
                this.ObjectSet.DeleteObject(entity);
            }
        }

        #region IRepository
        IQueryable IRepository.GetEntities()
        {
            return this.GetEntities();
        }

        public void Insert(object entity)
        {
            this.Insert((T)entity);
        }

        public void Update(object entity, object original)
        {
            this.Update((T)entity, (T)original);
        }

        public void Delete(object entity)
        {
            this.Delete((T) entity);
        }
        #endregion
        #region IUnitOfWork
        public void Save()
        {
            this.objectContext.SaveChanges();
        }
        #endregion
    }
}